﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using FFShellAPI.FFMPEG.Settings;
using FFShellAPI.FFMPEG.Core;
using System.Diagnostics;
using System.Collections.ObjectModel;
using MyLib.System.IO;
using System.IO;
using System.Xml.Linq;

namespace AVCWizard
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public static ConfigManager<AVCConfig> Config { get; set; }
        public static Dictionary<string, string> Translation { get; set; }

        AVCProcessHost analyser;
        AVCProcessHost encoder;
        RuntimeInfo rtinfo;
        string currentEncoderFile;
        string currentAnalyserFile;
        Stack<string> fileQueue;
        bool cancelFlag = false;

        const int devLevel = 0; // dev level: 0 = alpha version; 1 = beta version 2 >= normal release

        public MainWindow()
        {
            Config = new ConfigManager<AVCConfig>(this, ConfigDomain.UserDomain);
            Translation = DictionaryLoader.LoadDictionaryFromXml(
                StaticExt.AssemblyPath(StaticExt.LangFileName),
                Config.Current.AppCfg.LanguageId);
            if (Translation == null || Translation.Count == 0)
                Application.Current.Shutdown(128);
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
            fileQueue = new Stack<string>();
            XDocument xdoc = XDocument.Load(StaticExt.AssemblyPath("presets.xml"));
            PresetLoader presetLoader = new PresetLoader(xdoc.Root);
            OutputFormatCombo.Items.AddRangeSafe(presetLoader.Presets);
            ScreenFmtCombo.Items.AddRangeSafe(presetLoader.Resolutions);
            encoder = new AVCProcessHost(Config.Current.AppCfg.FFMpegPath);
            encoder.HandleProgressData += new HandleProgressDataEventHandler(encoder_HandleProgressData);
            encoder.EncodingFinished += new EncodingFinishedEventHandler(encoder_EncodingFinished);
            encoder.BeginEncoding += new EventHandler(encoder_BeginEncoding);
            analyser = new AVCProcessHost(Config.Current.AppCfg.FFMpegPath);
            analyser.EncodingFinished += new EncodingFinishedEventHandler(analyser_EncodingFinished);
            if (!Config.HasConfig)
            {
                QueryCodecs();
            }
            StartButton.IsEnabled = false;
            StartButton.Content = LocalTrans["Start"];
        }

        private void SelectResolution(int w, int h)
        {
            for (int i = 0; i < ScreenFmtCombo.Items.Count; ++i)
            {
                Resolution res = ScreenFmtCombo.Items[i] as Resolution;
                if (res.Width == w && res.Height == h)
                {
                    ScreenFmtCombo.SelectedIndex = i;
                    return;
                }
            }
            ScreenFmtCombo.SelectedIndex = -1;
        }

        private void MediaItemList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            MediaListItem item = ((ListView)sender).SelectedItem as MediaListItem;
            if (item != null)
            {
                SelectResolution(item.Video.PixelWidth, item.Video.PixelHeight);
            }
            CheckStartButtonEnabledState();
        }

        private void CheckStartButtonEnabledState()
        {
            MediaListItem item = MediaItemList.SelectedItem as MediaListItem;
            if (item != null)
                StartButton.IsEnabled = item.OutputFormatIndex >= 0 || encoder.ConvertionInProgress;
            else
                StartButton.IsEnabled = encoder.ConvertionInProgress;
        }

        private void ScreenFmtCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Resolution res = ((ComboBox)sender).SelectedItem as Resolution;
            if (res != null)
            {
                MediaListItem item = MediaItemList.SelectedItem as MediaListItem;
                if (item != null)
                {
                    item.VideoWidth = res.Width;
                    item.VideoHeight = res.Height;
                }
            }
        }

        private void ResTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            MediaListItem item = MediaItemList.SelectedItem as MediaListItem;
            if (item != null)
            {
                SelectResolution(item.Video.PixelWidth, item.Video.PixelHeight);
            }
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            if (Translation == null)
                this.Close();
            query_EncodingFinished(this, null);
            MediaItemList.ItemsSource = Config.Current.MediaList;
        }

        private void encoder_BeginEncoding(object sender, EventArgs e)
        {
            rtinfo = new RuntimeInfo();
            rtinfo.TimerEnabled = true;
        }

        private static double SecondsFromTimeSpan(TimeSpan time)
        {
            double dtime = time.Hours * 3600d + time.Minutes * 60d + time.Seconds;
            return dtime;
        }

        private void encoder_EncodingFinished(object sender, EncodingFinishedEventArgs args)
        {
            try
            {
                this.ExecuteThreadSafe(() =>
                   {
                       if (MediaItemList.Items.Count == 0)
                       {
                           ConvertProgressBar.Value = 0;
                           RemainingTimeText.Text = "00:00:00";
                           FileInfoText.Text = LocalTrans["NoOut"];
                           StartButton.Content = LocalTrans["Start"];
                           CheckStartButtonEnabledState();
                       }
                       else
                       {
                           if (!cancelFlag)
                               StartButton_Click(this, null);
                       }
                   });
                using (StreamWriter writer = new StreamWriter(Config.ConfigFilePath + "\\output.log"))
                {
                    writer.Write(args.ProcessOutput);
                }
                rtinfo.TimerEnabled = false;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
        }

        private void encoder_HandleProgressData(object sender, ProgressDataEventArgs args)
        {
            try
            {
                rtinfo.RawData = args.ProgressData;
                FileInfo finfo = new FileInfo(currentEncoderFile);
                string rtime = String.Format("{0:00}:{1:00}:{2:00}",
                    rtinfo.RemainingTime.Hours, rtinfo.RemainingTime.Minutes, rtinfo.RemainingTime.Seconds);
                this.ExecuteThreadSafe(() =>
                {
                    ConvertProgressBar.Value = rtinfo.Progress;
                    RemainingTimeText.Text = rtime;
                    FileInfoText.Text = finfo.Name + "; " + rtinfo.ToString();
                });
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
        }

        private void QueryCodecs()
        {
            try
            {
                AVCProcessHost query = new AVCProcessHost(Config.Current.AppCfg.FFMpegPath);
                query.EncodingFinished += new EncodingFinishedEventHandler(query_EncodingFinished);
                query.ProbeMedia("-codecs");
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
        }

        private void query_EncodingFinished(object sender, EncodingFinishedEventArgs args)
        {
            try
            {
                if (args != null)
                {
                    OutputParser parser = new OutputParser(args.ProcessOutput);
                    Config.Current.Codecs = parser.GetCodecs();
                }
                if (Config.Current.Codecs != null)
                {
                    AudioCodecComboBox.Items.AddRangeSafe(
                        AVCodec.Filter(Config.Current.Codecs,
                        CodecSupportFlags.AudioCodec | CodecSupportFlags.Encoding));
                    VideoCodecComboBox.Items.AddRangeSafe(
                        AVCodec.Filter(Config.Current.Codecs,
                        CodecSupportFlags.VideoCodec | CodecSupportFlags.Encoding));
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private void analyser_EncodingFinished(object sender, EncodingFinishedEventArgs args)
        {
            try
            {
                MediaListItem item = new MediaListItem();
                OutputParser parser = new OutputParser(args.ProcessOutput);
                bool hasAudio = parser.ExtractAudioFormat(item.Audio);
                bool hasVideo = parser.ExtractVideoFormat(item.Video);

                item.SrcAudioFormat = item.Audio.CodecName;
                item.SrcBitrate = item.Audio.Bitrate;
                item.SrcWidth = item.Video.PixelWidth;
                item.SrcHeight = item.Video.PixelHeight;
                item.SrcVideoFormat = item.Video.CodecName;
                item.SrcBitrate = parser.ExtractBitrate();

                TimeSpan timeVal = new TimeSpan();
                parser.ExtractDuration(ref timeVal);
                item.SrcLength = timeVal;
                FileInfo finfo = new FileInfo(currentAnalyserFile);
                item.SrcFileName = currentAnalyserFile;
                item.TargetFileName = Config.Current.AppCfg.TargetFolder + "\\" + finfo.Name;
                if (hasAudio || hasVideo)
                {
                    MediaItemList.ExecuteThreadSafe(() =>
                    {
                        Config.Current.MediaList.Add(item);
                    });
                }
                StartProbing();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
        }

        private void MediaItemList_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent("FileDrop", false))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;
        }

        private void MediaItemList_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent("FileDrop", false))
            {
                string[] paths = (string[])e.Data.GetData("FileDrop", false);
                foreach (string path in paths)
                {
                    fileQueue.Push(path);
                }
                StartProbing();
            }
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            if (!encoder.ConvertionInProgress)
            {
                try
                {
                    cancelFlag = false;
                    MediaListItem item = Config.Current.MediaList[0];
                    BaseSettings source = new BaseSettings
                    {
                        InputFile = item.SrcFileName,
                        OverrideExisting = true
                    };
                    currentEncoderFile = item.TargetFileName;
                    encoder.ConvertMedia(currentEncoderFile, source, item.Audio, item.Video);
                    Config.Current.MediaList.Remove(item);
                    StartButton.Content = LocalTrans["Stop"];
                }
                catch (ArgumentOutOfRangeException)
                {
                    return;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(LocalTrans["FFError"], LocalTrans["Error"], MessageBoxButton.OK, MessageBoxImage.Error);
                    Debug.WriteLine(ex.Message);
                    Debug.WriteLine(ex.StackTrace);
                }
            }
            else
            {
                cancelFlag = true;
                encoder.TerminateProcess();
            }
        }

        private void LogButton_Click(object sender, RoutedEventArgs e)
        {
            string notepad = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) + "\\notepad.exe";
            Process.Start(notepad, Config.ConfigFile("output.log"));
        }

        private void FormatComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            MediaListItem item = MediaItemList.SelectedItem as MediaListItem;
            FormatPreset preset = OutputFormatCombo.SelectedItem as FormatPreset;
            if (item != null && preset != null)
            {
                if (!preset.SupportVideo)
                {
                    item.EnableVideo = false;
                    VideoEnabledCheckBox.IsEnabled = false;
                }
                else
                {
                    VideoEnabledCheckBox.IsEnabled = true;
                }
                if (!preset.SupportAudio)
                {
                    item.EnableAudio = false;
                    AudioEnabledCheckBox.IsEnabled = false;
                }
                else
                {
                    AudioEnabledCheckBox.IsEnabled = true;
                }
                item.TargetFileName = preset.ReplaceFileExt(item.TargetFileName);
                if (preset.SupportAudio) preset.Apply(item.Audio);
                if (preset.SupportVideo) preset.Apply(item.Video);
                VideoCodecComboBox.GetBindingExpression(ComboBox.TextProperty).UpdateTarget();
                AudioCodecComboBox.GetBindingExpression(ComboBox.TextProperty).UpdateTarget();
                OutputFileInput.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            }
            CheckStartButtonEnabledState();
        }

        private void AddButton_Click(object sender, RoutedEventArgs e)
        {
            string[] fileNames = DialogTool.OpenDialogMultiSel(LocalTrans["FileFilter"], null);
            if (fileNames != null)
            {
                foreach (string fileName in fileNames)
                {
                    fileQueue.Push(fileName);
                }
            }
            StartProbing();
        }

        private void RemoveButton_Click(object sender, RoutedEventArgs e)
        {
            if (MediaItemList.SelectedItem != null)
                Config.Current.MediaList.Remove((MediaListItem)MediaItemList.SelectedItem);
        }

        private void TrashButton_Click(object sender, RoutedEventArgs e)
        {
            Config.Current.MediaList.Clear();
        }

        private void MoveDownButton_Click(object sender, RoutedEventArgs e)
        {
            if (MediaItemList.SelectedItem != null)
            {
                int index = Config.Current.MediaList.IndexOf((MediaListItem)MediaItemList.SelectedItem);
                if (index < (Config.Current.MediaList.Count - 1))
                    Config.Current.MediaList.Move(index, index + 1);
            }
        }

        private void MoveUpButton_Click(object sender, RoutedEventArgs e)
        {
            if (MediaItemList.SelectedItem != null)
            {
                int index = Config.Current.MediaList.IndexOf((MediaListItem)MediaItemList.SelectedItem);
                if (index > 0)
                    Config.Current.MediaList.Move(index, index - 1);
            }
        }

        private void StartProbing()
        {
            try
            {
                if (fileQueue.Count > 0)
                {
                    currentAnalyserFile = fileQueue.Pop();
                    BaseSettings target = new BaseSettings()
                    {
                        InputFile = currentAnalyserFile
                    };
                    analyser.ProbeMedia(target);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(LocalTrans["FFError"], LocalTrans["Error"], MessageBoxButton.OK, MessageBoxImage.Error);
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
        }

        private void VolSlider_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            ((Slider)sender).Value = 0;
        }

        public AVCConfig LocalConfig
        {
            get { return MainWindow.Config.Current; }
        }

        public Dictionary<string, string> LocalTrans
        {
            get { return MainWindow.Translation; }
        }

        private void SettingsButton_Click(object sender, RoutedEventArgs e)
        {
            AppSettings appsettings = new AppSettings();
            appsettings.Owner = this;
            if (appsettings.ShowDialog() == true)
            {
                Config.Current.AppCfg = appsettings.Cfg;
            }
        }

        private void BrowseButton_Click(object sender, RoutedEventArgs e)
        {
            MediaListItem item = MediaItemList.SelectedItem as MediaListItem;
            if (item != null)
            {
                string fname = DialogTool.SaveDialog(item.TargetFileName, LocalTrans["FileFilter"], "*.*");
                if (!String.IsNullOrWhiteSpace(fname))
                {
                    item.TargetFileName = fname;
                }
            }
        }

        private void PlayButton_Click(object sender, RoutedEventArgs e)
        {
            MediaListItem item = MediaItemList.SelectedItem as MediaListItem;
            if (item != null)
            {
                try
                {
                    AVCProcessHost player = new AVCProcessHost(Config.Current.AppCfg.FFMpegPath);
                    BaseSettings input = new BaseSettings()
                    {
                        InputFile = item.SrcFileName
                    };
                    player.PlayMedia(input);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(LocalTrans["FFError"], LocalTrans["Error"], MessageBoxButton.OK, MessageBoxImage.Error);
                    Debug.WriteLine(ex.Message);
                    Debug.WriteLine(ex.StackTrace);
                }
            }
        }

        private void BatchButton_Click(object sender, RoutedEventArgs e)
        {
            string fname = DialogTool.SaveDialog(LocalTrans["BatchFilter"], "*.*");
            if (!String.IsNullOrWhiteSpace(fname))
            {
                try
                {
                    using (StreamWriter outFile = new StreamWriter(File.Create(fname)))
                    {
                        foreach (MediaListItem item in Config.Current.MediaList)
                        {
                            BaseSettings source = new BaseSettings
                            {
                                InputFile = item.SrcFileName,
                                OverrideExisting = true
                            };
                            encoder.SaveConversionBatch(outFile, item.TargetFileName, source, item.Audio, item.Video);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(LocalTrans["FFError"], LocalTrans["Error"], MessageBoxButton.OK, MessageBoxImage.Error);
                    Debug.WriteLine(ex.Message);
                    Debug.WriteLine(ex.StackTrace);
                }
            }
        }

        private void AboutButton_Click(object sender, RoutedEventArgs e)
        {
            About aboutBox = new About(devLevel);
            aboutBox.Owner = this;
            aboutBox.ShowDialog();
        }
    }
}
